#include <iostream>
#include <stack>
#include <string>
#include <cmath>
#include <vector> 
#include <sstream>
using std::endl; 
using std::cout; 
using std::cin; 
using std::string; 
using std::stack;
using std::vector;

class calculator
{
	private:
		vector<string> postfix;
  
		int prior(char c);  //获取 运算符优先级	
		int prior_string(string c_string);		
		bool isOperator(char c);   //判断是数字还是运算符
		bool isOperator_string(string c_string);
  
		void getPostfix(const string& expr);  //中缀转后缀，顺便把每个数字拆出来换成string	
		double compute(); //计算后缀表达式
  
		bool isValid_part( );  //判断算式是否正确（一部分错误通过这个判断，另一部分在calculate()里判断）
  
		string toString(char c);//把一个字符转换成一个string，方便把操作符存入后缀表达式
		bool popTwoNumbers(stack<double>& s, double& first, double& second); //遇到二元操作以后从已经弹出的数字中取出最后两个 
		double stringToNum(const string& str) ;//把string转换为可以计算的double
		 
		
	public:
		calculator():errorCode(0){};
		int errorCode;
		double result;
		
		bool calculate( string &input );   //走流程的主函数
		int getErrorCode();
};

/**
*×走流程的主函数
*/
bool calculator::calculate( string &input )
{

	postfix.clear();//防止重复调用时出bug
	errorCode = 0;
	

	for(int i=0; i<input.size(); i++)
	{
		switch(input[i])
		{
			case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':case '.':case '+':case '-':case '*':case '/':case '^':case '(':case ')':case ' ':
				break;
			default:
				errorCode=5;
				cout<<"\n输入非法字符"<<endl;
				return false;								
		}
	}

	
	getPostfix(input);
	vector<string>::iterator itr;

/*
	cout<<"转换为后缀表达式为:";
	for( itr = postfix.begin(); itr!=postfix.end();itr++)
	{
		cout<< *itr <<"  ";
	}
*/

	result = compute();
	if( isValid_part() )	
	  cout<<"\n"<<input<<" = "<<result<<endl; 
	
	
	return true;
}

/**
*判断算式是否正确
*/
bool calculator::isValid_part()
{
	string tmp;
	vector<string>::iterator it;
	for( it=postfix.begin(); it!=postfix.end(); it++ )
	{
		tmp = *it;
		
		if(tmp == "(")     //后缀表达式中不会出现括号
			{errorCode=2;break;}
		
		if( (tmp.find(".")!=tmp.rfind("."))||tmp.find(".")==0 ) //一个数中出现两个小数点或者小数点在第一位
			{errorCode=3;break;}
		
	}
		
	if(errorCode)
	switch(errorCode)
	{
       	case 1:
	  cout<<"\n计算过程中出现0作除数"<<endl;
	  return false;
	case 2:
	  cout<<"\n括号不匹配"<<endl;
	  return false;
       	case 3:
	  cout<<"\n小数点的使用不符合语法"<<endl;
	  return false;
	case 4:
	  cout<<"\n操作符的使用不符合语法"<<endl;
	  return false;
			
       	default:
	  cout<<"\nerrorCode被人为改动，扔尝试输出计算结果"<<endl;
	  return true;
	}
	else
		return true;
}

/**
*弹出两个数字
*/
bool calculator::popTwoNumbers(stack<double>& s, double& first, double& second)
{
  if(s.empty())
    return false;

  first = s.top();
  s.pop();

  if(s.empty())
    return false;

  second = s.top();
  s.pop();

  return true;
}

/**
*把string转换为double
*/
double calculator::stringToNum(const string& str)  
{  
    std::istringstream iss(str);  
    double num;  
    iss >> num;  
    return num;      
} 

/**
*计算后缀表达式
*/
double calculator::compute() 
{
	double first,second;
	stack<double> s;
	for(int i=0; i<postfix.size(); ++i)
	{
		char c = postfix[i][0];
		switch (c)
		{
		case '+':
		  if( popTwoNumbers(s, first, second)==false)
		    {
		      errorCode = 4;
		      return 0;
		    }
			s.push(second+first);
			break;
		case '-':
		  if( popTwoNumbers(s, first, second)==false)
		    {
		      errorCode = 4;
		      return 0;
		    }
			s.push(second-first);
			break;
		case '*':
		  if( popTwoNumbers(s, first, second)==false)
		    {
		      errorCode = 4;
		      return 0;
		    }
			s.push(second*first);
			break;
		case '/':
		  if( popTwoNumbers(s, first, second)==false)
		    {
		      errorCode = 4;
		      return 0;
		    }
			if( first == 0 )   //除数为0
			{						
				errorCode = 1;
				return 0; 
			}
			s.push(second/first);
			break;
		case '^':
		  if(popTwoNumbers(s, first, second)==false)
		    {
		      errorCode = 4;
		      return 0;
		    }
			s.push(pow(second,first));
			break;
		default:
			s.push( stringToNum(postfix[i]) ); 
			break;
		}
	}
	double result = s.top();
	s.pop();
	return result;
}

/**
*把一个字符转换为string，方便把操作符存储进后缀表达式
*/
string calculator::toString(char c)
{
	string tmp;
	tmp +=c;
	return tmp;
} 
 
/**
*获取操作符的优先级
*/
int calculator::prior(char c) 
{
	switch (c)
	{
		case '+':
		case '-':
			return 1;
		case '*':
		case '/':
			return 2;
		case '^':
			return 3;
		default:
			return 0;
	}
}
int calculator::prior_string(string c_string)
{
	char c = c_string[0];
	switch (c)
	{
		case '+':
		case '-':
			return 1;
		case '*':
		case '/':
			return 2;
		case '^':
			return 3;
		default:
			return 0;
	}
}

/**
*判断是不是操作符
*/
bool calculator:: isOperator(char c)
{
	switch (c)
	{
		case '+':
		case '-':
		case '*':
		case '/':
		case '^':
			return true;
		default:
			return false;
	}
}
bool calculator:: isOperator_string(string c_string)
{
	char c=c_string[0];
	switch (c)
	{
		case '+':
		case '-':
		case '*':
		case '/':
		case '^':
			return true;
		default:
			return false;
	}
}

/**
*中缀转后缀
*顺便调整一下数字的存储方式
*/
void calculator::getPostfix(const string& expr)
{
	bool flagNum(true);  //用来记录当前的字符是不是double数字的第一位
	stack<string> s;  // 操作栈
	for(int i=0; i<expr.size(); ++i)
	{
		char c = expr[i];
		if(isOperator(c))
		{
			while(!s.empty() && isOperator_string(s.top()) && prior_string(s.top())>=prior(c))
			{
				postfix.push_back(s.top());
				s.pop();
			}
			
			s.push(toString(c));
			flagNum = true;    //操作符之后出现的第一个数字一定是double数字的第一位
		}
		else if(c == '(')
		{
			s.push(toString(c));
			flagNum = true;  
		}
		else if(c == ')')
		{
			while(s.top()[0] != '(')
			{
				postfix.push_back(s.top());
				s.pop();
			}
			s.pop();
		}
		else
		{
			if( flagNum ) 
			{
				postfix.push_back(toString(c));
				flagNum = false;  //存储完第一位之后的数字就不是了
			}	
			else
			{
				postfix.back() += c ;
			}
		}
	}
	
	while (!s.empty())
	{
		postfix.push_back(s.top());
		s.pop();
	}
	return;
}


int calculator::getErrorCode()
{
	return errorCode;	
}

